package com.fly.docker.web;

import java.util.Collections;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fly.core.entity.JsonResult;
import com.fly.docker.service.DockerService;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.DockerClient.LogsParam;
import com.spotify.docker.client.LogStream;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.ContainerConfig;
import com.spotify.docker.client.messages.ContainerCreation;
import com.spotify.docker.client.messages.HostConfig;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("/secretflow/psi")
@Api(tags = "隐语PSI接口")
public class SecretflowPsiApi
{
    @Autowired
    DockerService dockerService;
    
    /**
     * docker run -it --rm --network host --mount type=bind,source=/tmp/receiver,target=/root/receiver --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
     * --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.4.2b0 --config receiver/receiver.config
     * 
     * @param req
     * @return
     * @throws Exception
     */
    @ApiOperation("001-startReceiver")
    @PostMapping("/startReceiver")
    public JsonResult<?> startReceiver(@RequestBody @Valid PsiReceiver req)
        throws Exception
    {
        DockerClient dockerClient = dockerService.invokeAny();
        ContainerConfig containerConfig = ContainerConfig.builder()
            .attachStdin(true)
            .attachStdout(true)
            .attachStderr(true)
            .cmd("--config", req.getConfig())
            .tty(true)
            .openStdin(true)
            .stdinOnce(true)
            .image(req.getImage()) // 镜像
            .workingDir("/root")
            .networkDisabled(false)
            .hostConfig(HostConfig.builder()
                .networkMode("host") // 设置网络模式
                .appendBinds(req.getBindVolume()) // 磁盘映射
                .capAdd("SYS_PTRACE", "NET_ADMIN")
                .securityOpt("seccomp=unconfined")
                .privileged(true)
                // .autoRemove(true) // --rm
                .build())
            .build();
        ContainerCreation creation = dockerClient.createContainer(containerConfig);
        dockerClient.startContainer(creation.id());
        printContainLog(dockerClient, creation.id());
        return JsonResult.success(Collections.singletonMap("containerId", creation.id())).setMessage("启动容器成功！");
    }
    
    /**
     * docker run -it --rm --network host --mount type=bind,source=/tmp/sender,target=/root/sender --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
     * --cap-add=NET_ADMIN --privileged=true secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.4.2b0 --config sender/sender.config
     * 
     * @param req
     * @return
     * @throws Exception
     */
    @ApiOperation("002-startSender")
    @PostMapping("/startSender")
    public JsonResult<?> startSender(@RequestBody @Valid PsiSender req)
        throws Exception
    {
        DockerClient dockerClient = dockerService.invokeAny();
        ContainerConfig containerConfig = ContainerConfig.builder()
            .attachStdin(true)
            .attachStdout(true)
            .attachStderr(true)
            .cmd("--config", req.getConfig())
            .tty(true)
            .openStdin(true)
            .stdinOnce(true)
            .image(req.getImage()) // 镜像
            .workingDir("/root")
            .networkDisabled(false)
            .hostConfig(HostConfig.builder()
                .networkMode("host") // 设置网络模式
                .appendBinds(req.getBindVolume()) // 磁盘映射
                .capAdd("SYS_PTRACE", "NET_ADMIN")
                .securityOpt("seccomp=unconfined")
                .privileged(true)
                .build())
            .build();
        ContainerCreation creation = dockerClient.createContainer(containerConfig);
        dockerClient.startContainer(creation.id());
        printContainLog(dockerClient, creation.id());
        return JsonResult.success(Collections.singletonMap("containerId", creation.id())).setMessage("启动容器成功！");
    }
    
    /**
     * 打印日志
     * 
     * @param containerId
     * @throws DockerException
     * @throws InterruptedException
     */
    private void printContainLog(DockerClient dockerClient, String containerId)
        throws DockerException, InterruptedException
    {
        try (LogStream stream = dockerClient.logs(containerId, LogsParam.stdout(), LogsParam.stderr()))
        {
            String logs = stream.readFully();
            log.info("{}", logs);
        }
    }
}

@Data
@ApiModel(description = "PsiReceiver配置信息")
class PsiReceiver
{
    @NotBlank(message = "镜像名不能为空")
    @ApiModelProperty(value = "镜像名", example = "secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.4.2b0", required = true, position = 0)
    private String image;
    
    @NotBlank(message = "映射文件不能为空")
    @ApiModelProperty(value = "映射文件", example = "/tmp/receiver:/root/receiver", required = true, position = 1)
    private String bindVolume;
    
    @NotBlank(message = "配置文件不能为空")
    @ApiModelProperty(value = "配置文件", example = "receiver/receiver.config", required = true, position = 2)
    private String config;
}

@Data
@ApiModel(description = "PsiSender配置信息")
class PsiSender
{
    @NotBlank(message = "镜像名不能为空")
    @ApiModelProperty(value = "镜像名", example = "secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/psi-anolis8:0.4.2b0", required = true, position = 0)
    private String image;
    
    @NotBlank(message = "映射文件不能为空")
    @ApiModelProperty(value = "映射文件", example = "/tmp/sender:/root/sender", required = true, position = 1)
    private String bindVolume;
    
    @NotBlank(message = "配置文件不能为空")
    @ApiModelProperty(value = "配置文件", example = "sender/sender.config", required = true, position = 2)
    private String config;
}